﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Xml;

namespace Engine.Dtd.Schema {
    /// <summary>
    /// Represents an element of the DModelGroups object.
    /// </summary>
    [DebuggerDisplay("{Name} {Occurrence}")]
    public sealed class DElement : IModelGroupsNode, IModelGroupsElement, IEquatable<DElement> {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private DElementOccurrence occurrence;
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private DName name;

        /// <summary>
        /// Parse an occurrence character value.
        /// </summary>
        /// <param name="value">The occurrence character.</param>
        /// <returns>A DElementOccurence object.</returns>
        public static DElementOccurrence ParseOccurrence(char value) {
            switch (value) {
                case Characters.QueryMark:
                    return DElementOccurrence.ZeroOrOne;
                case Characters.Plus:
                    return DElementOccurrence.OneOrMore;
                case Characters.Asterix:
                    return DElementOccurrence.ZeroOrMore;
                default:
                    return DElementOccurrence.OnlyOnce;
                }
            }

        /// <summary>
        /// Initialize a new instance of the DElement class with specified name, occurrence and DTD declaration.
        /// </summary>
        /// <param name="name">A name of created element.</param>
        /// <param name="occurrence">An occurrence of the element.</param>
        /// <param name="declaration">A DTD declaration of the element.</param>
        /// <exception cref="ArgumentNullException">If the name or DTD declaration is null.</exception>
        public DElement(DName name, DElementOccurrence occurrence, DElementDeclaration declaration) {
            Guard.ArgumentNotNull(name, "name");
            Guard.ArgumentNotNull(declaration, "declaration");
            this.name = name;
            this.occurrence = occurrence;
            this.declaration = declaration;
            }

        /// <summary>
        /// Initialize a new instance of the DElement class with specified name and 
        /// occurrence type.
        /// </summary>
        /// <param name="name">A name of DTD element.</param>
        /// <param name="occurrence">An occurrence of the element.</param>
        internal DElement(DName name, DElementOccurrence occurrence) {
            Guard.ArgumentNotNull(name, "name");
            this.name = name;
            this.occurrence = occurrence;
            }

        /// <summary>
        /// Initialize a new instance of the DElement class with specified name
        /// and 'OnlyOnce' occurrence.
        /// </summary>
        /// <param name="name">A name of DTD element.</param>
        internal DElement(DName name)
            : this(name, DElementOccurrence.OnlyOnce) {
            }

        /// <summary>
        /// Gets the name of this element.
        /// </summary>
        public DName Name {
            get { return name; }
            }

        /// <summary>
        /// Gets the occurrence indicator for this element.
        /// </summary>
        public DElementOccurrence Occurrence {
            get { return occurrence; }
            internal set { occurrence = value; }
            }


        /// <summary>
        /// Determines whether this DElement object and the specified DElement object are equal.
        /// </summary>
        /// <param name="other">A DElement to compare to this instance.</param>
        /// <returns>true if the <c>other</c> is equal to this DElement object; otherwise false;</returns>
        public bool Equals(DElement other) {
            if (other == null)
                return false;
            else
                return name == other.name;
            }

        /// <summary>
        /// Determines whether this DElement object and the specified object are equal.
        /// </summary>
        /// <param name="obj">A object to compare to this instance.</param>
        /// <returns>true if the <c>obj</c> is equal to this DElement object.</returns>
        public override bool Equals(object obj) {
            DElement element = obj as DElement;
            if (element == null)
                return false;
            return Equals(element);
            }

        /// <summary>
        /// Returns the hash code for this DElement.
        /// </summary>
        /// <returns>An integer hash code.</returns>
        public override int GetHashCode() {
            return name.GetHashCode();
            }

        DName IModelGroupsElement.Name {
            get { return this.name; }
            }

        DElementOccurrence IModelGroupsElement.Occurrence {
            get { return this.occurrence; }
            }

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private DModelGroups parent;
        DModelGroups IModelGroupsElement.Parent {
            get { return parent; }
            }

        IModelGroupsNode IModelGroupsNode.FirstItem {
            get { return null; }
            }

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private int orderIndex;
        int IModelGroupsNode.OrderIndex {
            get { return orderIndex; }
            }

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private DElementDeclaration declaration;
        DElementDeclaration IModelGroupsNode.Declaration {
            get { return declaration; }
            }

        internal void SetDeclaration(DElementDeclaration declaration) {
            this.declaration = declaration;
            }

        internal void SetParent(DModelGroups parent) {
            this.parent = parent;
            }

        internal void Remove() {
            if (parent != null)
                parent.Remove(this);
            }

        internal void SetOrderIndex(int index) {
            this.orderIndex = index;
            }

        /// <summary>
        /// Returns a string that represents the current DTD element.
        /// </summary>
        /// <returns>A string that represents this element.</returns>
        public override string ToString() {
            string result = Name.ToString();
            switch (occurrence) {
                case DElementOccurrence.OnlyOnce:
                    break;
                case DElementOccurrence.OneOrMore:
                    result += Characters.Plus;
                    break;
                case DElementOccurrence.ZeroOrMore:
                    result += Characters.Asterix;
                    break;
                case DElementOccurrence.ZeroOrOne:
                    result += Characters.QueryMark;
                    break;
                default:
                    Debug.Fail("Unknown the occurrence of the current element object.");
                    break;
                }
            return result;
            }
        }
    }
